# -*- coding:utf-8 -*-
import tkinter as tk
from operator import index
from tkinter import filedialog

from boltons.iterutils import lstrip

import wordID
import wordDB

# 定义单词种类
word_type = ("keyword",     # 1  关键字
             "identifier",  # 2  标识符
             "unsigned",    # 3  无符号整数
             "operator",    # 4  运算符
             "delimiter",   # 5  界符
             "escape",      # 7  转义字符
             "unknown",     # 8  其他字符
             "start",       # 9  默认开始状态
             "comment",     # 10 注释
             "isdelimiter_signal", # 11 分隔符
             "string",      # 字符串
             )
# 创建单词种类字典<种类:识别码>
tmp_dict = {}
for item in range(len(word_type)):
    tmp_dict[word_type[item]] = item+1
print(tmp_dict)
word_type = tmp_dict
# 注释
comment = 0
# 行号
line_index = 0
# 单词状态
word_type_status = wordID.word_type["start"]
# 存储单个单词(单词缓冲区)
word_tmp = ""
# 存储单词的字典(单词库)
word_DB = {}
#日志输出开关
logout = 1
#跳过下个字符匹配的开关(默认关闭)
skip_next_char = 0

# logout = 0
# 打开需要编译的py文件
def open_file():
    file = filedialog.askopenfilename(title="选择py文件",
                                      filetypes=[("py文件","*.py")])
    # file = "D:/01l/work/code/python/002py/wordID.py"

    #try语句用于解决编码问题
    try:
        if file:
            print(f"当前文件路径:{file}\n")
            with open(file, "r", encoding="utf-8") as f:
                text_lines = f.readlines()  # 使用repr显示原始字符串
                # text = type(text)
                logger(logout, f"当前文件行数:{len(text_lines)}")
                return text_lines
        else:
            return 404
    except UnicodeDecodeError:
        if file:
            print(f"当前文件路径:{file}\n")
            with open(file, "r") as f:
                text_lines = f.readlines()  # 使用repr显示原始字符串
                # text = type(text)
                logger(logout, f"当前文件行数:{len(text_lines)}")
                return text_lines
        else:
            return 404

# 匹配程序
def match_word(text_lines, char_start=0, line_start=0):
    for line in range(line_start, len(text_lines)):
        # 正在扫描的行号
        wordID.line_index+=1
        line_ = repr(text_lines[line])[1:]
        line_repr = line_[:-1].lstrip() #需要匹配的行 (带转义字符)
        # 如果这一行是空行,则跳过匹配
        if line_repr == "\\n":
            logger(logout,f"外层循环 第{wordID.line_index}是空行,跳过匹配\n")
            line_start += 1
            return match_word(text_lines, char_start, line_start)
        logger(logout,f"外层循环 第{wordID.line_index}行  内容:\"{line_[:-1].lstrip()}\" 当前行长度:{len(line_repr)}")

        # 开始匹配
        # 如果去除空格首行字符是 # 这说明这一行是一整行注释
        # lstrip可以去除这一行左边的空格
        if lstrip(line_)[0]== '#':
            wordID.word_type_status = wordID.word_type["comment"]
            wordID.comment+=1
            logger(logout,f"外层循环 这一行是注释 种别码:{word_type_status} 内容:\"{line_[:-1]}\" "
                  f"当前共有{wordID.comment}条注释\n")
            #这一行结束,直接从下一行开始
            line_start+=1
            wordID.line_index = line_start #用于输出日志中的行数
            wordID.word_type_status = wordID.word_type["start"]
            return match_word(text_lines, char_start, line_start)
        # 不是注释

        # 内层循环
        # 开始进行行内遍历
        # line_repr = line_[:-1].lstrip() #需要匹配的行 (带转义字符)
        logger(logout,f"当前行长度:{len(line_repr)} 内容:{line_repr}")
        for char_index in range(char_start, len(line_repr)):
            # 判断当前字符是否需要跳过
            if wordID.skip_next_char:
                wordID.skip_next_char = 1
                continue

            if line_repr[-2:] == "\\n": # 判断这一行是否有转行符号,没有则不用进行转行的处理
                # 用于匹配倒数第二个字符,如果最后两个字符是转行,则跳过匹配
                if line_repr[char_index] + line_repr[char_index + 1] == "\\n":
                    line_start += 1
                    match_word_tmp()  # 单词匹配函数
                    logger(logout,"该行结束\n")
                    return match_word(text_lines, char_start, line_start)

            line_repr_char = line_repr[char_index] #当前需要匹配的字符
            logger(logout,f"行内测试 内容:{line_repr_char} 当前char_index : {char_index}")

            # 判断是否是分隔符
            if (wordDB.isdelimiter_signal(line_repr_char) or
                wordDB.isoperator(line_repr_char)
                ):
                if line_repr_char == ' ':
                    wordID.logger(logout,f'当前匹配的符号是 空格 进入匹配')
                # 如果该字符是运算符,则加入单词缓冲区进行匹配
                if wordDB.isoperator(line_repr_char):
                    wordID.logger(logout,f'当前匹配的符号是 {line_repr_char} 运算符 进入匹配  ')
                    # wordID.word_tmp += line_repr_char #这里好像有问题
                # 如果该字符不是运算符,则可能是其他符号,先进行缓冲区匹配,在进行该符号匹配
                match_word_tmp() # 单词缓冲区匹配
                wordID.word_tmp += line_repr_char
                match_word_tmp() # 识别到的字符匹配
                continue

            # 判断代码后面的注释
            if line_repr_char == "#":
                # 代码后面直接全部识别成注释,直接跳过这行
                wordID.word_type_status = wordID.word_type["comment"]
                wordID.comment += 1
                wordID.logger(logout,f"剩下的内容是注释 当前共有{wordID.comment}条注释\n")
                # 这一行结束,直接从下一行开始
                line_start += 1
                wordID.line_index = line_start  # 用于输出日志中的行数
                wordID.word_type_status = wordID.word_type["start"]
                return match_word(text_lines, char_start, line_start)

            # 判断是否是字母
            if line_repr_char.isalpha():
                # 判断最后两个字符是否是转行
                # 单词缓冲区添加这个字母
                wordID.word_tmp+= line_repr_char
                continue

            # 判断是否是下划线
            if line_repr_char in ['_']:
                # 判断最后两个字符是否是转行
                # 单词缓冲区添加这个字母
                wordID.word_tmp+= line_repr_char
                continue

            # 判断是否是数字
            if line_repr_char.isdigit():
                # 判断最后两个字符是否是转行
                # 单词缓冲区添加
                wordID.logger(logout,f"{line_repr_char} 是数字")
                wordID.word_tmp+= line_repr_char
                continue

            # 判断是否是界符,字符串
            if wordDB.isdelimiter(line_repr_char) in ["Ldelimiter", "Rdelimiter","delimiter"]:
                # 测试日志
                wordID.logger(logout,f"分隔符匹配:{line_repr_char}")
                # 首先需要判断是不是字符串
                if line_repr_char in ["\"","\'"]:
                    #如果缓冲区内为空,则说明后续字符可能是字符串
                    if wordID.word_tmp == '':
                        wordID.word_tmp += line_repr_char
                        continue

                    # 判断缓冲区第一个字符内有没有 " ,有则将该 " 添加止单词缓冲区,最终会被识别成字符串
                    if wordID.word_tmp[0] == line_repr_char:
                        wordID.word_tmp += line_repr_char
                        match_word_tmp()
                        continue

                # 如果是界符则先判断单词缓冲区中的单词
                match_word_tmp()
                # 界符添加至缓冲区,在进行界符的匹配
                wordID.word_tmp+= line_repr_char
                match_word_tmp()
                continue

            # 判断是否是转义字符 " \ "
            if line_repr_char == "\\":
                wordID.logger(logout,f"匹配到 \\ 可能是转义字符,跳过下个字符匹配")
                # 判断最后两个字符是否是转行
                # 读取下一个字符,一起添加到里面
                wordID.word_tmp+= line_repr_char + line_[char_index + 1]
                # 这里需要匹配这个字符和下一个字符,需要添加一个标记来跳过下一个字符的匹配
                # 法2: 把行内测试单独写成函数,直接递归
                # 当前采用的是第一种方法
                wordID.skip_next_char = 1 #跳过下个字符
                match_word_tmp() # 匹配当前缓冲区

                continue


        # 行内遍历完成之后，进行下一行遍历
        line_start += 1
    # 打印识别出的字符
    wordID.logger(logout,word_DB)
    # 运行完成后之后重置
    wordID.line_index = 0
    wordID.comment = 0
    wordID.word_type_status = wordID.word_type["start"]
    return (word_DB)

# 识别单词缓冲区的算法
def match_word_tmp():
    # 判断为空 日志输出
    if wordID.word_tmp == '':
        wordID.logger(logout,f"单词缓冲区为空,跳过单词识别")
        # 重置缓冲区
        wordID.word_tmp = ""
        return

    # 如果是空格则日志输出 "空格" 直观易读
    wordID.logger(logout,f"当前单词缓冲区:{wordID.word_tmp}" if word_tmp != " " else "当前单词缓冲区:空格")

    # 判断是否是字符串 判断是否是以 ' " 标识的字符串
    if word_tmp[0] in ["\"","\'"] and word_tmp[0] == word_tmp[-1]:
        wordID.logger(logout,f"{word_tmp} 录入成功,字符串,{wordID.word_type["string"]}")
        # 将关键字添加至单词库 "单词本身":"单词种类编号"
        wordID.word_DB[wordID.word_tmp] = word_type["string"]
        # 重置缓冲区
        wordID.word_tmp = ""
        return

    # 判断是否是关键字
    if wordDB.iskeyword(wordID.word_tmp):
        wordID.logger(logout,f"{word_tmp} 录入成功,关键字,{wordID.word_type["keyword"]}")
        # 将关键字添加至单词库 "单词本身":"单词种类编号"
        wordID.word_DB[wordID.word_tmp] = word_type["keyword"]
        # 重置缓冲区
        wordID.word_tmp = ""
        return

    # 判断是否是界符
    if wordDB.isdelimiter(wordID.word_tmp) in ["Ldelimiter","Rdelimiter","delimiter"]:
        wordID.logger(logout,f"{word_tmp} 录入成功,界符,{wordID.word_type["delimiter"]}")
        wordID.word_type_status = wordID.word_type["delimiter"]
        # 将关键字添加至单词库 "单词本身":"单词种类编号"
        wordID.word_DB[wordID.word_tmp] = word_type["delimiter"]
        # 重置缓冲区
        wordID.word_tmp = ""
        return

    # 判断是否是分隔符
    if wordDB.isdelimiter_signal(wordID.word_tmp):
        wordID.word_type_status = wordID.word_type["isdelimiter_signal"]
        # 如果是空格则换成中文,方便日志查看
        if wordID.word_tmp == " ":
            wordID.logger(logout, f"\"空格\" 录入成功,分隔符,{wordID.word_type["isdelimiter_signal"]}")
            wordID.word_DB["空格"] = word_type["isdelimiter_signal"]
            # 重置缓冲区
            wordID.word_tmp = ""
            return

        # 将关键字添加至单词库 "单词本身":"单词种类编号"
        wordID.word_DB[wordID.word_tmp] = word_type["isdelimiter_signal"]
        wordID.logger(logout,f"\"{word_tmp}\" 录入成功,分隔符,{wordID.word_type["isdelimiter_signal"]}")
        # 重置缓冲区
        wordID.word_tmp = ""
        return

    # 判断是否是运算符
    if wordDB.isoperator(wordID.word_tmp):
        wordID.logger(logout,f"{word_tmp} 录入成功,运算符,{wordID.word_type["operator"]}")
        wordID.word_type_status = wordID.word_type["operator"]
        # 将关键字添加至单词库 "单词本身":"单词种类编号"
        wordID.word_DB[wordID.word_tmp] = word_type["operator"]
        # 重置缓冲区
        wordID.word_tmp = ""
        return

    # 查看是否是转义字符
    if wordDB.isescape(wordID.word_tmp):
        wordID.logger(logout, f"{word_tmp} 录入成功,转义符,{wordID.word_type["escape"]}")
        wordID.word_type_status = wordID.word_type["escape"]
        # 将关键字添加至单词库 "单词本身":"单词种类编号"
        wordID.word_DB[wordID.word_tmp] = word_type["escape"]
        # 重置缓冲区
        wordID.word_tmp = ""
        return

    # 数字
    if word_tmp.isdigit():
        wordID.logger(logout, f"{word_tmp} 录入成功,数字,{wordID.word_type["unsigned"]}")
        wordID.word_type_status = wordID.word_type["unsigned"]
        # 将关键字添加至单词库 "单词本身":"单词种类编号"
        wordID.word_DB[wordID.word_tmp] = word_type["unsigned"]
        # 重置缓冲区
        wordID.word_tmp = ""
        return

    # 判断是否是变量(标识符) 字母数字和下划线组成,数字不能开头
    #先判断单词是不是数字开头
    if word_tmp[0] not in ["0","1","2","3","4","5","6","7","8","9"]:
        target = 1
        # 判断是否只包涵字母下划线
        for i in word_tmp:
            if i.isalpha() or i.isdigit():
                continue
            target = False
        if target:
            # 这种情况则说明该单词是标识符
            wordID.logger(logout,f"{word_tmp} 录入成功,标识符")
            wordID.word_type_status = wordID.word_type["identifier"]
            # 将关键字添加至单词库 "单词本身":"单词种类编号"
            wordID.word_DB[wordID.word_tmp] = word_type["identifier"]
            # 重置缓冲区
            wordID.word_tmp = ""
            return

    # 测试
    wordID.word_tmp = ""

# 带开关的print函数,用于日志输出
def logger(logout,content):
    if logout:
        print(content)

